package com.kdyzm.demo.springboot.config.plugin;

import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetWrapper;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.util.*;

/**
 * @author kdyzm
 * @date 2022/2/16
 */
@Slf4j
public class ResultExtWrapper {

    private Configuration configuration;

    private ResultSetWrapper resultSetWrapper;

    private ResultMap resultMap;

    private Object rowValue;

    private final Map<String, List<UnMappedColumnMapping>> unMappedColumnMappingCache = new HashMap<>();

    public ResultExtWrapper(Configuration configuration,
                            ResultSetWrapper resultSetWrapper,
                            ResultMap resultMap,
                            Object rowValue) {
        this.configuration = configuration;
        this.resultSetWrapper = resultSetWrapper;
        this.resultMap = resultMap;
        this.rowValue = rowValue;
    }

    /**
     * 额外填充其它属性值
     *
     * @return 填充后的值
     * @see DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap, java.lang.String)
     */
    public Object extFillData() {
        List<UnMappedColumnMapping> unMappedColumnMapping = getUnMappedColumnMapping(resultSetWrapper, resultMap);
        for (UnMappedColumnMapping mappedColumnMapping : unMappedColumnMapping) {
            Field field = mappedColumnMapping.getField();
            String columnName = mappedColumnMapping.getColumnName();
            TypeHandler<?> typeHandler = mappedColumnMapping.getTypeHandler();
            try {
                field.setAccessible(true);
                Object fieldValue = field.get(rowValue);
                if (Objects.nonNull(fieldValue)) {
                    log.debug("跳过字段{}的赋值", columnName);
                    continue;
                }
                Object result = typeHandler.getResult(resultSetWrapper.getResultSet(), columnName);
                field.set(rowValue,result);
            } catch (IllegalAccessException | SQLException e) {
                log.error("", e);
            }
        }
        return rowValue;
    }


    private List<UnMappedColumnMapping> getUnMappedColumnMapping(ResultSetWrapper firstResultSet, ResultMap resultMap) {
        Class clazz = resultMap.getType();
        List<UnMappedColumnMapping> unMappedColumnMappings = this.unMappedColumnMappingCache.get(clazz.getName());
        if (!CollectionUtils.isEmpty(unMappedColumnMappings)) {
            return unMappedColumnMappings;
        }
        unMappedColumnMappings = new ArrayList<>();

        Set<String> mappedProperties = resultMap.getMappedProperties();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (Modifier.isFinal(field.getModifiers())
                || Modifier.isStatic(field.getModifiers())
                || Modifier.isVolatile(field.getModifiers())
            ) {
                continue;
            }
            String fieldName = field.getName();
            boolean contains = mappedProperties.contains(fieldName);
            if (contains) {
                continue;
            }
            TableField annotation = field.getAnnotation(TableField.class);
            if (Objects.isNull(annotation)) {
                continue;
            }
            String columnName = annotation.value().trim().replaceAll("`","");
            TypeHandler<?> typeHandler = firstResultSet.getTypeHandler(field.getType(), columnName);
            if (Objects.isNull(typeHandler)) {
                log.error("不支持的字段反序列化：{}", columnName);
            } else {
                log.debug("字段={}使用的反序列化工具为：{}", columnName, typeHandler);
                UnMappedColumnMapping unMappedColumnMapping = new UnMappedColumnMapping(
                    field,
                    columnName,
                    field.getName(),
                    typeHandler
                );
                unMappedColumnMappings.add(unMappedColumnMapping);
            }
        }
        this.unMappedColumnMappingCache.put(clazz.getName(), unMappedColumnMappings);
        return unMappedColumnMappings;
    }


    @Data
    @AllArgsConstructor
    static class UnMappedColumnMapping {

        private Field field;

        private String columnName;

        private String propertyName;

        private TypeHandler<?> typeHandler;
    }
}
